<template>
  <div class="rule-opera">
    <div class="opera-head">
      <div class="head-title">{{ pageTitle }}</div>
      <div class="head-btn">
        <el-button size="small" type="primary" @click="validRule"
          >验证规则</el-button
        >
        <el-button size="small" type="primary" @click="saveRule"
          >保存规则</el-button
        >
        <el-button size="small" type="primary" @click="$router.go(-1)">
          返回
        </el-button>
      </div>
    </div>
    <div class="opera-main">
      <el-form
        v-loading="isLoading"
        ref="ruleFormRef"
        :rules="rules"
        :model="ruleForm"
        label-width="140px"
        style="width: 80%"
      >
        <el-form-item label="规则名称：" prop="name">
          <el-input v-model="ruleForm.name" placeholder="请输入规则名称" />
        </el-form-item>
        <!-- 关联本体选择后 请求对应的本体类别和本体关系 -->
        <el-form-item label="关联本体：" prop="relatedOntologyId">
          <el-select
            v-model="ruleForm.relatedOntologyId"
            placeholder="请选择本体"
            filterable
            style="width: 100%"
            @change="handleOntologyChange"
          >
            <el-option
              v-for="item in ontologyList"
              :key="item.ontologyAllId"
              :label="item.ontologyAllName"
              :value="item.ontologyAllId"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="规则描述：" prop="description">
          <el-input
            v-model="ruleForm.description"
            type="textarea"
            :rows="3"
            placeholder="请输入规则描述"
          />
        </el-form-item>
        <el-form-item label="规则配置：" class="bold"> </el-form-item>
        <el-form-item label="条件：" prop="ruleCondition">
          <div class="config-wrap">
            <div
              class="config-item"
              :class="c.flag"
              v-for="c in conditions"
              :key="c.label"
            >
              <template v-if="c.flag === 'node'">
                <div class="node-char">{{ c.char }}</div>
                <div class="node-label">{{ c.label }}</div>
              </template>
              <template v-if="c.flag === 'edge'">
                <div class="edge-label">{{ c.label }}</div>
                <div class="edge-arrow"></div>
              </template>
            </div>
            <div class="config-btn">
              <i
                class="el-icon-circle-plus"
                :class="{ 'is-disable': conditions.length >= 5 }"
                @click="addCondition"
              ></i>
              <i
                class="el-icon-remove"
                v-if="conditions.length !== 0"
                @click="removeCondition"
              ></i>
            </div>
          </div>
        </el-form-item>
        <el-form-item label="结论：" prop="conclusion">
          <div class="config-wrap">
            <div
              class="config-item"
              :class="c.flag"
              v-for="c in conclusions"
              :key="c.label"
            >
              <template v-if="c.flag === 'node'">
                <div class="node-char">{{ c.char }}</div>
                <div class="node-label">{{ c.label }}</div>
              </template>
              <template v-if="c.flag === 'edge'">
                <div class="edge-label">{{ c.label }}</div>
                <div class="edge-arrow"></div>
              </template>
            </div>
            <div class="config-btn">
              <i
                class="el-icon-circle-plus"
                :class="{ 'is-disable': ruleForm.conclusion }"
                @click="addConclusion"
              ></i>
              <i
                class="el-icon-remove"
                v-if="ruleForm.conclusion"
                @click="removeConclusion"
              ></i>
            </div>
          </div>
        </el-form-item>
      </el-form>
    </div>
    <!-- 配置条件 -->
    <el-dialog title="配置条件" :visible.sync="conditionVisible" width="35%">
      <el-form
        ref="conditionForm"
        :model="curCondition"
        label-width="140px"
        :rules="conditionRules"
      >
        <el-form-item
          :label="isStart ? '推理关系起点：' : '关系起点：'"
          prop="from"
        >
          <el-select
            v-model="curCondition.from"
            filterable
            :placeholder="isStart ? '请选择推理关系起点' : '请选择关系起点'"
            :disabled="!isStart"
            style="width: 100%"
            @change="filterRelation"
          >
            <el-option
              v-for="item in entityClassList"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="实体类别：" prop="to">
          <el-select
            v-model="curCondition.to"
            filterable
            placeholder="请选择实体类别"
            style="width: 100%"
            @change="filterRelation"
          >
            <el-option
              v-for="item in entityClassList"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="关系：" prop="edge">
          <el-select
            v-model="curCondition.edge"
            filterable
            placeholder="请选择关系"
            :disabled="!(curCondition.from && curCondition.to)"
            style="width: 100%"
          >
            <el-option
              v-for="item in availableRelations"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="conditionVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmCondition">确定</el-button>
      </template>
    </el-dialog>

    <!-- 配置结论 -->
    <el-dialog title="配置结论" :visible.sync="conclusionVisible" width="35%">
      <el-form
        ref="conclusionForm"
        :model="curConclusion"
        label-width="140px"
        :rules="conclusionRules"
      >
        <el-form-item label="推理结论起点：" prop="from">
          <el-select
            v-model="curConclusion.from"
            filterable
            style="width: 100%"
            disabled
            @change="filterInferenceRelations"
          >
            <el-option
              v-for="item in inferenceOptions"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="推理结论终点：" prop="to">
          <el-select
            v-model="curConclusion.to"
            filterable
            style="width: 100%"
            disabled
            @change="filterInferenceRelations"
          >
            <el-option
              v-for="item in inferenceOptions"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
        <el-form-item label="关系：" prop="edge">
          <el-select
            v-model="curConclusion.edge"
            placeholder="请选择关系"
            allow-create
            filterable
            default-first-option
            :disabled="!(curConclusion.from && curConclusion.to)"
            style="width: 100%"
          >
            <el-option
              v-for="item in inferenceRelations"
              :key="item.id"
              :label="item.name"
              :value="item.id"
            >
            </el-option>
          </el-select>
        </el-form-item>
      </el-form>
      <template #footer>
        <el-button @click="conclusionVisible = false">取消</el-button>
        <el-button type="primary" @click="confirmConclusion">确定</el-button>
      </template>
    </el-dialog>
    <SelectGraph ref="selectGraphRef" @confirm="submitValid" />
  </div>
</template>
<script>
import SelectGraph from "./components/SelectGraph.vue";

export default {
  name: "CompletionRuleOpera",
  components: {
    SelectGraph,
  },
  data() {
    return {
      // 字母标识
      letterStr: "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
      ruleForm: {
        name: null,
        relatedOntologyId: null,
        description: null,
        ruleCondition: null,
        conclusion: null,
      },
      rules: {
        name: [
          { required: true, message: "规则名称不能为空！", trigger: "blur" },
        ],
        relatedOntologyId: [
          { required: true, message: "关联本体不能为空！", trigger: "blur" },
        ],
        ruleCondition: [
          { required: true, message: "条件不能为空！", trigger: "blur" },
        ],
        conclusion: [
          { required: true, message: "结论不能为空！", trigger: "blur" },
        ],
      },
      // 本体列表
      ontologyList: [],
      // 本体类查询条件
      entitySearchParams: {
        ontologyState: "0",
        pageNum: 1,
        pageSize: 1000,
      },
      // 本体类列表
      entityClassList: [],
      // 本体关系
      relationSearchParams: {
        relationState: "0",
        pageNum: 1,
        pageSize: 1000,
      },
      // 本体关系列表
      relationList: [],
      // 配置条件中可以选择的关系
      availableRelations: [],
      // 条件
      conditions: [
        // {
        //   id: null,
        //   label: "人物",
        //   char: "A",
        //   flag: "node",
        // },
        // {
        //   id: null,
        //   label: "同事",
        //   flag: "edge",
        // },
      ],
      conditionRules: {
        from: [{ required: true, message: "起点不能为空！", trigger: "blur" }],
        edge: [{ required: true, message: "关系不能为空！", trigger: "blur" }],
        to: [
          { required: true, message: "实体类别不能为空！", trigger: "blur" },
        ],
      },
      // 配置条件弹窗
      conditionVisible: false,
      // 标识配置条件的类型 isStart为true时包含推理起点，否则为后续添加
      isStart: true,
      // 当前配置条件
      curCondition: {
        from: null,
        edge: null,
        to: null,
      },
      conclusions: [],
      // 当前配置结论
      curConclusion: {
        from: null,
        edge: null,
        to: null,
      },
      // 结论起点终点下拉选项
      inferenceOptions: [],
      // 结论关系下拉选项
      inferenceRelations: [],
      // 结论弹窗校验规则
      conclusionRules: {
        from: [
          {
            required: true,
            message: "推理结论起点不能为空！",
            trigger: "blur",
          },
        ],
        edge: [{ required: true, message: "关系不能为空！", trigger: "blur" }],
        to: [
          {
            required: true,
            message: "推理结论终点不能为空！",
            trigger: "blur",
          },
        ],
      },
      // 配置结论弹窗
      conclusionVisible: false,
      // 编辑时回显数据需要转换 添加loading
      isLoading: false,
    };
  },
  computed: {
    pageTitle() {
      return this.$route.query.id ? "编辑补全规则" : "新增补全规则";
    },
    // 条件列表最后一项
    conditionLastItem() {
      return this.conditions[this.conditions.length - 1];
    },
  },
  created() {
    this.getAllOntology();
    if (this.$route.query.id) {
      this.getRuleDetail();
    }
  },
  methods: {
    // 获取全部本体模型
    getAllOntology() {
      this.$axios
        .get(this.$api.ontologyModel.xinbiaoObj.searchall)
        .then((res) => {
          if (res.data.code !== 200) {
            this.$message.error(res.data.message);
          } else {
            this.ontologyList = res.data.data;
          }
        })
        .catch();
    },
    // 获取本体模型下的本体类
    getOntologyClass() {
      return new Promise((resolve, reject) => {
        this.$axios
          .get(this.$api.ontologyModel.xinbiaoObj.searchontology, {
            params: {
              ontologyAllId: this.ruleForm.relatedOntologyId,
              ...this.entitySearchParams,
            },
          })
          .then((res) => {
            if (res.data.code !== 200) {
              this.$message.error(res.data.message);
            } else {
              this.entityClassList = res.data.data.list || [];
            }
            resolve();
          })
          .catch((e) => {
            reject(e);
          });
      });
    },
    // 本体类关系
    getOntologyRelation() {
      return new Promise((resolve, reject) => {
        this.$axios
          .get(this.$api.ontologyModel.xinbiaoObj.searchRealation, {
            params: {
              ontologyAllId: this.ruleForm.relatedOntologyId,
              ...this.relationSearchParams,
            },
          })
          .then((res) => {
            if (res.data.code !== 200) {
              this.$message.error(res.data.message);
            } else {
              this.relationList = res.data.data.list || [];
            }
            resolve();
          })
          .catch((e) => reject(e));
      });
    },

    // 验证规则
    validRule() {
      if (!this.ruleForm.ruleCondition || !this.ruleForm.conclusion) {
        return this.$message.warning("请先配置规则");
      }
      this.$refs.selectGraphRef.openDialog();
    },
    submitValid(graphId) {
      this.$axios
        .post(
          this.$api.knowledgeComputation.completionRule.verifyCompletionRule,
          {
            ...this.ruleForm.ruleCondition,
            ...this.ruleForm.conclusion,
            graphId,
          }
        )
        .then((res) => {
          if (res.data.code !== 200) {
            this.$message.error(res.data.message);
          } else {
            this.$message.success(res.data.message);
          }
        })
        .catch();
    },
    // 保存规则
    saveRule() {
      this.$refs.ruleFormRef.validate((valid) => {
        if (valid) {
          if (this.$route.query.id) {
            this.editSubmit();
          } else {
            this.addSubmit();
          }
        }
      });
    },
    editSubmit() {
      let params = { ...this.ruleForm };
      params.ruleCondition = JSON.stringify(params.ruleCondition);
      params.conclusion = JSON.stringify(params.conclusion);
      this.$axios
        .put(
          this.$api.knowledgeComputation.completionRule.editCompletionRule,
          params
        )
        .then((res) => {
          if (res.data.code !== 200) {
            this.$message.error(res.data.message);
          } else {
            this.$message.success(res.data.message);
            this.$router.go(-1);
          }
        })
        .catch();
    },
    addSubmit() {
      let params = { ...this.ruleForm };
      params.ruleCondition = JSON.stringify(params.ruleCondition);
      params.conclusion = JSON.stringify(params.conclusion);
      this.$axios
        .post(
          this.$api.knowledgeComputation.completionRule.addCompletionRule,
          params
        )
        .then((res) => {
          if (res.data.code !== 200) {
            this.$message.error(res.data.message);
          } else {
            this.$message.success(res.data.message);
            this.$router.go(-1);
          }
        })
        .catch();
    },
    handleOntologyChange() {
      this.getOntologyClass();
      this.getOntologyRelation();
      // 切换本体时 置空配置
      this.ruleForm.ruleCondition = null;
      this.ruleForm.conclusion = null;
      this.conditions = [];
      this.conclusions = [];
    },

    // 添加条件
    // 条件为空时 有推理关系起点选择 标识状态 isStart: true
    // 已经存在一层推导时,标识状态 isStart: false 后续弹窗的起点都直接绑定且禁用
    addCondition() {
      if (!this.ruleForm.relatedOntologyId) {
        return this.$message.warning("请先选择关联本体");
      }
      if (this.conditions.length === 0) {
        this.isStart = true;
        this.curCondition = {
          from: null,
          edge: null,
          to: null,
        };
        this.conditionVisible = true;
      } else {
        this.isStart = false;
        // 目前此功能仅支持两层关系推导 因此只在conditions长度为0和3的时候响应
        if (this.conditions.length === 3) {
          this.curCondition = {
            from: this.conditionLastItem.id,
            edge: null,
            to: null,
          };
          this.conditionVisible = true;
        }
      }
    },
    // 移除条件
    removeCondition() {
      if (this.conditions.length) {
        this.conditions.pop();
        this.conditions.pop();
        if (this.conditions.length === 1) {
          this.conditions.pop();
        }
      }
      this.removeConclusion();
    },
    // 选择起点和实体类别时 筛选关系
    filterRelation() {
      if (this.curCondition.from && this.curCondition.to) {
        let from, to;
        this.entityClassList.forEach((e) => {
          if (e.id === this.curCondition.from) {
            from = e;
          }
          if (e.id === this.curCondition.to) {
            to = e;
          }
        });

        this.availableRelations = this.relationList.filter(
          (i) => i.sourceName === from.name && i.targetName === to.name
        );
      }
    },
    // 选择推理起点和推理终点时 筛选关系
    filterInferenceRelations() {
      if (this.curConclusion.from && this.curConclusion.to) {
        let from, to;
        this.entityClassList.forEach((e) => {
          if (e.id === this.curConclusion.from) {
            from = e;
          }
          if (e.id === this.curConclusion.to) {
            to = e;
          }
        });

        this.inferenceRelations = this.relationList.filter(
          (i) => i.sourceName === from.name && i.targetName === to.name
        );
      }
    },

    // 添加结论
    addConclusion() {
      // 条件如果还未生成 则不可配置结论
      if (this.conditions.length < 5) { // !this.ruleForm.ruleCondition
        return this.$message.warning("请先配置规则条件");
      }
      if (!this.ruleForm.conclusion) {
        this.curConclusion = {
          from: null,
          edge: null,
          to: null,
        };
        const tmpOpt = [];
        this.conditions.forEach((i) => {
          if (i.flag === "node") {
            tmpOpt.push({ name: i.char, id: i.id });
          }
        });
        this.inferenceOptions = tmpOpt;
        this.curConclusion.from = tmpOpt[0].id;
        this.curConclusion.to = tmpOpt[tmpOpt.length -1].id;
        this.conclusionVisible = true;
      }
    },

    // 移除结论
    removeConclusion() {
      this.ruleForm.conclusion = null;
      this.conclusions = [];
    },

    // 配置条件确定
    confirmCondition() {
      this.$refs.conditionForm.validate((valid) => {
        if (!valid) return;
        if (this.isStart) {
          // 起始
          let fromItem, toItem;
          this.entityClassList.forEach((e) => {
            if (e.id === this.curCondition.from) {
              fromItem = e;
            }
            if (e.id === this.curCondition.to) {
              toItem = e;
            }
          });
          let edgeItem = this.availableRelations.find(
            (i) => i.id === this.curCondition.edge
          );
          this.conditions.push(
            {
              id: fromItem.id,
              label: fromItem.name,
              char: "A",
              flag: "node",
            },
            {
              id: edgeItem.id,
              label: edgeItem.name,
              flag: "edge",
            },
            {
              id: toItem.id,
              label: toItem.name,
              char: "B",
              flag: "node",
            }
          );
        } else {
          let edgeItem = this.availableRelations.find(
            (i) => i.id === this.curCondition.edge
          );
          let toItem = this.entityClassList.find(
            (i) => i.id === this.curCondition.to
          );
          let charIdx = Math.floor((this.conditions.length + 2) / 2);
          this.conditions.push(
            {
              id: edgeItem.id,
              label: edgeItem.name,
              flag: "edge",
            },
            {
              id: toItem.id,
              label: toItem.name,
              char: this.letterStr[charIdx],
              flag: "node",
            }
          );
        }
        // 当前仅支持两层推导, 数组长度为5时，生成条件规则
        if (this.conditions.length === 5) {
          const res = {};
          let arrLen = this.conditions.length;
          let nodeNum = 0;
          let edgeNum = 0;
          for (let i = 0; i < arrLen; i++) {
            let item = this.conditions[i];
            if (item.flag === "node") {
              nodeNum++;
              res[`ontology${nodeNum}`] = item.label;
              res[`ontology${nodeNum}Id`] = item.id;
            }
            if (item.flag === "edge") {
              edgeNum++;
              res[`relation${nodeNum}`] = item.label;
              res[`relation${nodeNum}Id`] = item.id;
            }
          }
          this.ruleForm.ruleCondition = res;
        }
        this.conditionVisible = false;
      });
    },
    // 配置结论确定
    confirmConclusion() {
      this.$refs.conclusionForm.validate((valid) => {
        if (!valid) return;
        let fromItem, toItem;
        this.entityClassList.forEach((e) => {
          if (e.id === this.curConclusion.from) {
            fromItem = e;
          }
          if (e.id === this.curConclusion.to) {
            toItem = e;
          }
        });
        let edgeItem = this.inferenceRelations.find(
          (i) => i.id === this.curConclusion.edge
        );
        // 用于展示
        let fromChar = this.inferenceOptions.find(
          (i) => i.id === this.curConclusion.from
        );
        let toChar = this.inferenceOptions.find(
          (i) => i.id === this.curConclusion.to
        );
        this.conclusions.push(
          {
            id: fromItem.id,
            label: fromItem.name,
            char: fromChar?.name,
            flag: "node",
          },
          {
            id: edgeItem ? edgeItem.id : null,
            label: edgeItem ? edgeItem.name : this.curConclusion.edge,
            flag: "edge",
          },
          {
            id: toItem.id,
            label: toItem.name,
            char: toChar?.name,
            flag: "node",
          }
        );
        // 生成结论
        this.ruleForm.conclusion = {
          ontologyStart: fromItem.name,
          ontologyStartId: fromItem.id,
          relation: edgeItem ? edgeItem.name : this.curConclusion.edge,
          relationId: edgeItem ? edgeItem.id : null,
          ontologyEnd: toItem.name,
          ontologyEndId: toItem.id,
        };
        this.conclusionVisible = false;
      });
    },

    // 编辑逻辑
    getRuleDetail() {
      this.isLoading = true;
      this.$axios
        .get(
          this.$api.knowledgeComputation.completionRule.getCompletionRuleDetail,
          {
            params: { id: this.$route.query.id },
          }
        )
        .then((res) => {
          if (res.data.code !== 200) {
            this.$message.error(res.data.message);
            this.isLoading = false;
          } else {
            const resData = res.data.data;
            // TODO 回显规则配置
            try {
              resData.ruleCondition = JSON.parse(resData.ruleCondition);
              resData.conclusion = JSON.parse(resData.conclusion);
              Object.assign(this.ruleForm, resData);
              this.transRuleInfo();
            } catch (error) {
              this.isLoading = false;
              console.error(error);
            }
          }
        })
        .catch(() => {
          this.isLoading = false;
        });
    },
    // 回显信息
    async transRuleInfo() {
      try {
        await this.getOntologyClass();
        await this.getOntologyRelation();
        let keysLen = 0;
        if (typeof this.ruleForm.ruleCondition === "object") {
          keysLen = Object.keys(this.ruleForm.ruleCondition).length;
        }
        const tmpConditions = [];
        const tmpCondition = this.ruleForm.ruleCondition;
        const idCharMap = {};
        for (let i = 1; i < keysLen; i++) {
          // 节点
          let node = { flag: "node", char: this.letterStr[i - 1] };
          if (`ontology${i}` in tmpCondition) {
            node.label = tmpCondition[`ontology${i}`];
          }
          if (`ontology${i}Id` in tmpCondition) {
            node.id = tmpCondition[`ontology${i}Id`];
          }
          // 边
          let edge = { flag: "edge" };
          if (`relation${i}` in tmpCondition) {
            edge.label = tmpCondition[`relation${i}`];
          }
          if (`relation${i}Id` in tmpCondition) {
            edge.id = tmpCondition[`relation${i}Id`];
          }

          if (node.label || node.id) {
            idCharMap[node.id] = node.char;
            tmpConditions.push(node);
          }
          if (edge.label || edge.id) {
            tmpConditions.push(edge);
          }
        }
        this.conditions = tmpConditions;
        const tmpConclusion = this.ruleForm.conclusion;
        this.conclusions = [
          {
            id: tmpConclusion.ontologyStartId,
            label: tmpConclusion.ontologyStart,
            char: idCharMap[tmpConclusion.ontologyStartId],
            flag: "node",
          },
          {
            id: tmpConclusion.relationId,
            label: tmpConclusion.relation,
            flag: "edge",
          },
          {
            id: tmpConclusion.ontologyEndId,
            label: tmpConclusion.ontologyEnd,
            char: idCharMap[tmpConclusion.ontologyEndId],
            flag: "node",
          },
        ];
      } catch {}
      this.isLoading = false;
    },
  },
};
</script>
<style lang="scss" scoped>
.rule-opera {
  background: #fff;
}

/deep/ .el-form-item.bold .el-form-item__label {
  font-weight: bold;
}

.opera-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 12px 32px;
  border-bottom: 1px solid #ddd;
  .head-title {
    font-size: 16px;
    font-weight: bold;
  }
}
.opera-main {
  padding: 32px;
}

.config-btn {
  margin-left: 10px;
  display: inline-block;
  .el-icon-circle-plus {
    margin: 0 5px;
    font-size: 16px;
    color: $--color-primary;
    cursor: pointer;
    &.is-disable {
      color: #909399;
    }
  }
  .el-icon-remove {
    margin: 0 5px;
    font-size: 16px;
    color: $--color-danger;
    cursor: pointer;
  }
}

.config-wrap {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
}

.config-item {
  line-height: 1.5;
  .node-char {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 35px;
    width: 35px;
    border: 1px solid;
    border-radius: 50%;
    margin: 5px;
  }
  .node-label {
    text-align: center;
  }
  &.edge {
    width: 70px;
    text-align: center;
    align-self: flex-start;
  }
  .edge-label {
    margin: 5px 5px 0;
  }
  .edge-arrow {
    position: relative;
    height: 2px;
    background-color: #333;
    &::before {
      content: "";
      position: absolute;
      right: 0;
      width: 10px;
      height: 2px;
      background-color: #333;
      transform-origin: right;
      transform: rotateZ(-30deg);
    }
    &::after {
      content: "";
      position: absolute;
      right: 0;
      width: 10px;
      height: 2px;
      background-color: #333;
      transform-origin: right;
      transform: rotateZ(30deg);
    }
  }
}
</style>
